/*
 * @copyright: Huang Ding
 * @Author: ding-cx
 * @Date: 2021-03-10 09:20:47
 * @LastEditors: ding-cx
 * @LastEditTime: 2021-04-06 15:52:02
 * @Description: file content
 */

import { Agent } from "egg";
import { EggWebSocket, IPCData } from "../../ws";
import WebSocketServer from "./WsServer";
import { handleWsSendData } from "../util";
import * as streams from "stream";
import { buffer_to_stream } from "@diyaner/dy-utils/libs/node/files";
import * as fs from "fs";
import streamwormhole from "stream-wormhole";
import WebSocket = require("ws");
import * as uuid from "uuid";

export function streamWriteToFile(
  readStream: fs.ReadStream,
  writeFilePath: string
): Promise<true | any> {
  return new Promise((resolve, reject) => {
    try {
      const writeStream = fs.createWriteStream(writeFilePath);
      readStream.pipe(writeStream);

      readStream.on("end", () => {
        // console.log("end");
        writeStream.end();
        // writeStream.close();
        resolve(true);
      });
    } catch (error) {
      // 若发生错误，将上传文件流消费掉。
      streamwormhole(readStream);
      reject(error);
    }
  });
}
const errMsg = [
  "消息必须是对象，可被json parse。",
  "消息对象需提供event，wsuid字段。",
];

export const onMessage = (
  wss: WebSocketServer,
  ws: EggWebSocket,
  agent: Agent
) => {
  ws.on("message", (msg: WebSocket.Data) => {
    // console.log(msg);

    // debugger;
    // 如果是buffer的文件 demo
    if (msg instanceof Buffer) {
      const stream = buffer_to_stream(msg) as fs.ReadStream;
      const tempFileName = uuid.v4();
      streamWriteToFile(stream, "D:/uploads/" + tempFileName)
        .then((res) => {
          if (res) {
            ws.send(
              handleWsSendData({
                event: "send-file",
                temp: tempFileName,
                state: "success",
              })
            );
          } else {
            ws.send(handleWsSendData({ event: "send-file", state: "error" }));
          }
        })
        .catch((error) => {
          console.log(error);
        });

      return;
    }
    //   收到消息应预处理。
    let msgData = {} as IPCData;
    try {
      msgData = JSON.parse(msg.toString());
      if (typeof msgData !== "object") {
        ws.send(handleWsSendData({ is_error: true, msg: errMsg[0] }));
        ws.close(
          undefined,
          handleWsSendData({ is_error: true, msg: errMsg[0] })
        );
        return;
      }
    } catch (error) {
      ws.close(undefined, handleWsSendData({ is_error: true, msg: errMsg[0] }));
      return;
    }
    if (!msgData.event || !msgData.wsuid) {
      ws.close(
        undefined,
        handleWsSendData({
          is_error: true,
          msg: errMsg[1],
        })
      );
      return;
    }
    // END 预处理结束。

    // TODO 一些如获取当前在线列表之类的消息，则直接内部处理，不用转发业务层。
    if (msgData.event === "user-list") {
      msgData.data = wss.getPools();
      ws.send(handleWsSendData(msgData));
      return;
    }

    // 使用者只需监听二者之一的事件进行业务处理。
    // 通用业务，转发业务层 app worker
    agent.messenger.sendRandom("ws-message", msgData);
    // 通用业务 触发agent work上的事件。
    wss.work.emit(msgData.event, msgData);
  });
};

/** ws模块的接口可在worker中监听进程消息，也可在agent中通过暴露的实例进行监听。可自己选择在哪个进程操作，但不可同时响应。否则容易重复执行业务逻辑。 */
export const onSend = (wss: WebSocketServer, agent: Agent) => {
  // 某个ws回复信息，在worker接口中会直接携带wsuid回来。适合请求响应模式。
  const response = (data: IPCData) => {
    const ws = wss.pools[data.wsuid];
    // console.log(ws);
    // console.log(wss.pools);
    ws?.send(handleWsSendData(data));
  };
  wss.work.on("ws-response", response);
  agent.messenger.on("ws-response", response);

  //   全部广播信息
  const broadcast = (data: IPCData) => {
    wss.broadcast(data);
  };
  wss.work.on("ws-broadcast", broadcast);
  agent.messenger.on("ws-broadcast", broadcast);

  //   本房间内发送信息
  const room = (data: IPCData) => {
    const ws = wss.pools[data.wsuid];
    const room = wss.rooms[ws !== undefined ? ws.room : ""];
    // console.log(data.wsuid);
    // console.log(ws);
    // console.log(room);
    if (room) {
      const sendData = handleWsSendData(data);

      Object.keys(room).forEach((item) => {
        room[item]?.send(sendData);
      });
    }
  };
  agent.messenger.on("ws-room", room);
  wss.work.on("ws-room", room);

  //   给其他人发送信息，1个或多个均可
  const send = (data: IPCData) => {
    data.wsuids?.forEach((item) => {
      wss.pools[item]?.send(handleWsSendData(data));
    });
  };
  agent.messenger.on("ws-send", send);
  wss.work.on("ws-send", send);
};
